package com.gwt.client.ui;

import java.util.List;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.requestfactory.shared.EntityProxyChange;
import com.google.gwt.requestfactory.shared.EntityProxyId;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.WriteOperation;
import com.google.gwt.requestfactory.ui.client.EntityProxyKeyProvider;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy.KeyboardPagingPolicy;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.RangeChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.gwt.client.event.EditBeerEvent;
import com.gwt.client.event.SlideTransitionEvent;
import com.gwt.client.ui.widget.editor.BeerEditorWorkflow;
import com.gwt.shared.BeerProxy;
import com.gwt.shared.BeerTableRequestFactory;

public class Slide12 extends Composite implements Slide {

	private static final Binder binder = GWT.create(Binder.class);

	interface Binder extends UiBinder<Widget, Slide12> {
		/***/
	}

	static class BeerCell extends AbstractCell<BeerProxy> {

		@Override
		public void render(com.google.gwt.cell.client.Cell.Context context, BeerProxy value, SafeHtmlBuilder sb) {
			if (value == null) {
				return;
			}

			sb.appendHtmlConstant("<div>");
			sb.appendHtmlConstant("<div><b>Name: </b>" + value.getName() + "</div>");
			sb.appendHtmlConstant("<div><b>Style: </b>" + value.getStyleName() + "</div><hr/>");
			sb.appendHtmlConstant("</div>");
		}
	}

	private Slide.Presenter listener;
	private EventBus eventBus;
	private final BeerTableRequestFactory requestFactory;
	private final ListDataProvider<BeerProxy> items;
	private final SingleSelectionModel<BeerProxy> selectionModel;
	private Receiver<List<BeerProxy>> lastReceiver;
	private int lastFetch;
	private int currentBeerCount = 30;

	@UiField
	FocusPanel slide;

	@UiField(provided = true)
	CellList<BeerProxy> beerList;

	@UiField
	Button generateBeerButton;

	public Slide12(EventBus eventBus) {
		this.eventBus = eventBus;
		this.requestFactory = GWT.create(BeerTableRequestFactory.class);

		requestFactory.initialize(eventBus);

		beerList = new CellList<BeerProxy>(new BeerCell());
		items = new ListDataProvider<BeerProxy>(new EntityProxyKeyProvider<BeerProxy>());
		beerList.setKeyboardPagingPolicy(KeyboardPagingPolicy.INCREASE_RANGE);

		selectionModel = new SingleSelectionModel<BeerProxy>();
		beerList.setSelectionModel(selectionModel);
		selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {

			@Override
			public void onSelectionChange(SelectionChangeEvent event) {
				Slide12.this.eventBus.fireEvent(new EditBeerEvent(selectionModel.getSelectedObject()));
			}
		});

		BeerEditorWorkflow.register(eventBus, requestFactory);

		EntityProxyChange.registerForProxyType(eventBus, BeerProxy.class, new EntityProxyChange.Handler<BeerProxy>() {

			@Override
			public void onProxyChange(EntityProxyChange<BeerProxy> event) {
				Slide12.this.onBeerChanged(event);
			}
		});

		initWidget(binder.createAndBindUi(this));
	}

	@Override
	protected void onLoad() {
		slide.setFocus(true);

		fetchBeer(0);
	}

	@UiHandler("slide")
	public void keyPress(KeyDownEvent e) {
		eventBus.fireEvent(new SlideTransitionEvent(e));
	}

	@UiHandler("generateBeerButton")
	public void onGenerateMoreBeer(ClickEvent event) {
		currentBeerCount += 30;
		fetchBeer(currentBeerCount);
	}

	public void setPresenter(Slide.Presenter listener) {
		this.listener = listener;
	}

	void onBeerChanged(EntityProxyChange<BeerProxy> event) {
		if (WriteOperation.PERSIST.equals(event.getWriteOperation())) {
			if (beerList.isRowCountExact()) {
				fetchBeer(lastFetch + 1);
			}
		}

		if (WriteOperation.UPDATE.equals(event.getWriteOperation())) {
			EntityProxyId<BeerProxy> beerId = event.getProxyId();

			int displayOffset = offsetOf(beerId);
			if (displayOffset != -1) {
				requestFactory.find(beerId).fire(new Receiver<BeerProxy>() {

					@Override
					public void onSuccess(BeerProxy beer) {
						int offset = offsetOf(beer.stableId());
						if (offset != -1) {
							beerList.setRowData(beerList.getPageStart() + offset, java.util.Collections.singletonList(beer));
						}
					}
				});
			}
		}
	}

	@UiHandler("beerList")
	void onRangeChange(RangeChangeEvent event) {
		Range r = event.getNewRange();
		fetchBeer(r.getStart());
	}

	private void fetchBeer(final int start) {
		lastFetch = start;

		requestFactory.beerListRequest().getBeerList(start, currentBeerCount).fire(new Receiver<List<BeerProxy>>() {

			@Override
			public void onSuccess(List<BeerProxy> response) {
				if (lastFetch != start) {
					return;
				}

				int responses = response.size();
				System.out.println(responses);
				beerList.setPageSize(responses);
				beerList.setRowData(start, response);
				if (start == 0 || !beerList.isRowCountExact()) {
					beerList.setRowCount(start + responses, responses < currentBeerCount);
				}
			}

		});
	}

	private int offsetOf(EntityProxyId<BeerProxy> beerId) {
		List<BeerProxy> displayedItems = beerList.getVisibleItems();
		for (int offset = 0, j = displayedItems.size(); offset < j; offset++) {
			if (beerId.equals(displayedItems.get(offset).stableId())) {
				return offset;
			}
		}
		return -1;
	}
}